package cn.tedu.upload.service.impl;

import cn.tedu.exception.ServiceException;
import cn.tedu.upload.mapper.UploadMapper;
import cn.tedu.upload.pojo.entity.Upload;
import cn.tedu.upload.service.UploadService;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.io.ResourceLoader;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;

import java.io.IOException;
import java.io.InputStream;
import java.math.BigInteger;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.UUID;

@Service
public class UploadServiceImpl implements UploadService {

    // 文件存储的相对路径（相对于resources目录）
    private static final String UPLOAD_RELATIVE_PATH = "upload/src/main/resources/static/upload";
    // 日期格式，用于构造文件存储路径
    private static final String DATE_FORMAT = "/yyyyMMdd/";
    // 本地主机URL，用于构造文件访问URL
    private static final String LOCALHOST_URL = "http://127.0.0.1:8093/";

    @Autowired
    private UploadMapper uploadMapper;

    // 注入资源加载器，用于访问资源目录
    @Autowired
    private ResourceLoader resourceLoader;

    private static final Logger logger = LoggerFactory.getLogger(UploadServiceImpl.class);

    @Override
    public Upload upload(MultipartFile file) {
        // 计算文件的MD5值
        String md5 = null;
        try {
            md5 = getMd5(file);
        } catch (NoSuchAlgorithmException | IOException e) {
            throw new ServiceException(400, e.getMessage());
        }

        // 查找数据库中是否存在相同MD5值的文件记录
        Upload existingUpload = findUploadByMd5(md5);
        if (existingUpload != null) {
            return existingUpload; // 如果存在相同的文件记录，直接返回
        }

        // 验证文件名是否为空或无效
        validateFileName(file.getOriginalFilename());

        // 生成唯一的文件名
        String fileName = generateUniqueFileName(file.getOriginalFilename());

        // 构建文件存储路径
        String filePath = constructFilePath(fileName);

        // 创建文件存储目录（如果不存在）
        createDirectoriesIfNotExist(filePath);

        // 保存文件到指定路径
        saveFile(file, filePath);

        // 创建上传实体对象
        Upload upload = createUploadEntity(file, md5, filePath, fileName);

        // 将文件信息插入数据库
        uploadMapper.insert(upload);

        return upload;
    }

    // 根据MD5值查找数据库中的文件记录
    private Upload findUploadByMd5(String md5) {
        LambdaQueryWrapper<Upload> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(Upload::getMd5, md5);
        return uploadMapper.selectOne(queryWrapper);
    }

    // 验证文件名是否为空或无效
    private void validateFileName(String fileName) {
        if (fileName == null || fileName.trim().isEmpty()) {
            throw new ServiceException(422, "获取文件名失败");
        }
    }

    // 生成唯一的文件名
    private String generateUniqueFileName(String originalFileName) {
        String suffix = originalFileName.substring(originalFileName.lastIndexOf("."));
        return UUID.randomUUID() + suffix;
    }

    // 构建文件存储路径
    private String constructFilePath(String fileName) {
        String datePath = formatDate(new Date());
        // 构建相对路径
        Path relativePath = Paths.get(UPLOAD_RELATIVE_PATH + datePath + fileName);
        return relativePath.toString();
    }

    // 创建文件存储目录（如果不存在）
    private void createDirectoriesIfNotExist(String filePath) {
        Path path = Paths.get(filePath);
        Path parentDir = path.getParent();
        try {
            // 创建必要的目录
            Files.createDirectories(parentDir);
        } catch (IOException e) {
            logger.error("Failed to create directory: {}", parentDir.toAbsolutePath(), e);
            throw new ServiceException(400, "创建文件失败");
        }
    }

    // 保存文件到指定路径
    private void saveFile(MultipartFile file, String filePath) {
        Path path = Paths.get(filePath);
        try {
            // 将文件保存到指定路径
            Files.copy(file.getInputStream(), path);
        } catch (IOException e) {
            logger.error("Failed to save file to path {}: {}", filePath, e.getMessage(), e);
            throw new ServiceException(400, "文件保存失败: " + e.getMessage());
        }
    }

    // 创建上传实体对象
    private Upload createUploadEntity(MultipartFile file, String md5, String filePath, String fileName) {
        Upload upload = new Upload();
        // 确保URL使用正斜杠
        upload.setUrl((LOCALHOST_URL + "upload" + formatDate(new Date()) + fileName));
        upload.setFile(file.getOriginalFilename());
        upload.setSize(file.getSize());
        upload.setMd5(md5);
        upload.setFileName(fileName);
        upload.setMimeType(file.getContentType());
        upload.setCreateTime(new Date());
        return upload;
    }

    // 计算文件的MD5值
    private String getMd5(MultipartFile file) throws NoSuchAlgorithmException, IOException {
        MessageDigest md = MessageDigest.getInstance("MD5");
        try (InputStream is = file.getInputStream()) {
            byte[] buffer = new byte[4096];
            int length;
            while ((length = is.read(buffer)) != -1) {
                md.update(buffer, 0, length);
            }
        }
        byte[] digest = md.digest();
        return new BigInteger(1, digest).toString(16).toUpperCase();
    }

    // 格式化日期
    private String formatDate(Date date) {
        SimpleDateFormat sdf = new SimpleDateFormat(DATE_FORMAT);
        // 替换掉格式字符串中的反斜杠
        String formattedDate = sdf.format(date);
        return formattedDate.replace("\\", "/");
    }
}